package worldinruin.controllers;


import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.HashMap;

import javax.swing.AbstractAction;

import worldinruin.GameDataHandler;
import worldinruin.core.IBuilding;
import worldinruin.core.IPlayer;
import worldinruin.core.IResources;
import worldinruin.core.ISoldier;
import worldinruin.core.ITile;
import worldinruin.core.ITroop;
import worldinruin.core.imp.Resources;
import worldinruin.core.imp.Troop;
import worldinruin.core.imp.battle.Battle;
import worldinruin.gui.imp.gameview.DivideTroopsPanel;
import worldinruin.gui.imp.gameview.TrainSoldiersPanel;
import worldinruin.gui.imp.gameview.gameboard.ITilePiece;
import worldinruin.gui.imp.gameview.gameboard.TroopPiece;
import worldinruin.gui.imp.gameview.gameboard.tiles.TileView;
import worldinruin.resources.Components;
import worldinruin.resources.Keys;
import worldinruin.resources.Panels;

public class GameBoardController extends AbstractAction implements MouseListener {
	
	private Component lastClickedComponent;
	private int lastClickedMouseButton;
	private ITile currentTile;
	private IPlayer currentPlayer;
	
	

	/**
	 * Constructs a building on the gameboard, and checks if player can afford
	 * the purchase.
	 * @param building Building to construct.
	 */
	private void constructBuilding(IBuilding building) {
		currentPlayer = GameDataHandler.getInstance().getGame().getActivePlayer();
		
		IResources price = building.getPrice();
		
		if (currentPlayer.canAfford(price)) {
			currentPlayer.subtractFromResources(price);
			currentTile.addBuilding(building);
			currentPlayer.addBuilding(building);
			GameDataHandler.getInstance().getGui().updateMessage("You have contructed a "
					+ building.getBuildingType().toString().toLowerCase() + ".");
		}
		else {
			GameDataHandler.getInstance().getGui().updateMessage("You do not have enough resources!");
		}
	}
	
	/**
	 * Divide an army into two troops.
	 * @param troop Initial army to divide.
	 * @param troop1Values First troop.
	 * @param troop2Values Second troop.
	 */
	private void divideTroop(ITroop troop, HashMap<ISoldier, Integer> troop1Values, HashMap<ISoldier, Integer> troop2Values) {
		currentTile.removeTroop(troop);
		
		ITroop troop1 = new Troop(GameDataHandler.getInstance().getGame().getActivePlayer(), currentTile);
		ITroop troop2 = new Troop(GameDataHandler.getInstance().getGame().getActivePlayer(), currentTile);
		
		for (ISoldier s : troop1Values.keySet()) {
				troop1.add(s, troop1Values.get(s));
		}
		
		for (ISoldier s : troop2Values.keySet()) {
			troop2.add(s, troop2Values.get(s));	
		}
		
		GameDataHandler.getInstance().getGui().updateMessage("You have divided your army.");
		
	}

	/**
	 * Train troops at a city or a barrack.
	 * @param values How many of each soldier type to train.
	 */
	private void trainTroop(int[] values) {
		currentPlayer = GameDataHandler.getInstance().getGame().getActivePlayer();
		IResources price = new Resources(0,0,0,0);
		
		ArrayList<ISoldier> soldierTypes = GameDataHandler.getInstance().getSoldierTypes();
		
		for (int c = 0; c < soldierTypes.size(); c++) {
			price = price.add(soldierTypes.get(c).getPrice().multiply(values[c]));
		}
		
		if(price.equals(new Resources(0, 0, 0, 0))) {
			return;
		}
		
		else if(currentPlayer.canAfford(price)){
			
			ITroop t = new Troop(currentPlayer, currentTile);
			
			for (int c = 0; c < soldierTypes.size(); c++) {
				t.add(soldierTypes.get(c), values[c]);
			}
			
			currentPlayer.subtractFromResources(price);
			GameDataHandler.getInstance().getGui().updateMessage("You have trained units.");
			
		}
		else {
			GameDataHandler.getInstance().getGui().updateMessage("You do not have enough resources!");
		}

	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		
		if (e.getSource() == getValue(Components.trainSoldiersButton)) {
			
			int[] values = ((TrainSoldiersPanel) getValue(Keys.UNITAMOUNT.toString())).getValues();
			
			trainTroop(values);
		}
		
		else if (e.getSource() == getValue(Components.buildViewButton)) {
			ITile tile = (ITile) getValue(Keys.BUILDINGVIEWKEY.toString());
			PanelController.getInstance().showPage(Panels.GAMEVIEWLEFTCARDSTACK, Panels.BUILDPANEL, tile);
		}
		
		else if (e.getSource() == getValue(Components.buildBuildingButton)) {
			IBuilding building = (IBuilding) getValue(Keys.BUILDINGKEY.toString());
			constructBuilding(building);
		}
		
		else if (e.getSource() == getValue(Components.trainSoldiersViewButton)) {
			PanelController.getInstance().showPage(Panels.GAMEVIEWLEFTCARDSTACK, Panels.TRAINSOLDIERSPANEL);
		}
		
		else if (e.getSource() == getValue(Components.divideTroopViewButton)) {
			ITroop troop = (Troop) getValue(Keys.DIVIDETROOPVIEW.toString());
			PanelController.getInstance().showPage(Panels.GAMEVIEWLEFTCARDSTACK, Panels.DIVIDETROOPSPANEL, troop);
		}
		
		else if (e.getSource() == getValue(Components.divideTroopButton)) {
			DivideTroopsPanel divideTroopsPanel = (DivideTroopsPanel) getValue(Keys.DIVIDETROOP.toString());
			
			HashMap<ISoldier, Integer> troop1Values = divideTroopsPanel.getTroop1Values();
			HashMap<ISoldier, Integer> troop2Values = divideTroopsPanel.getTroop2Values();
			ITroop troop = divideTroopsPanel.getTroop();
			
			divideTroop(troop, troop1Values, troop2Values);
		}
	}

	@Override
	public void mouseClicked(MouseEvent arg) {
		Object o = arg.getSource();
		int mouseButtonClicked = arg.getButton();
		currentPlayer = GameDataHandler.getInstance().getGame().getActivePlayer();
		// Logic sequence that tests for troop movement & starting a battle sequence(s)

			if (lastClickedMouseButton == MouseEvent.BUTTON1 && o.getClass() == new TileView().getClass() 
					&& mouseButtonClicked == MouseEvent.BUTTON3) {
				
				ITroop troop = (Troop) ((TroopPiece) lastClickedComponent).getPieceModel();
			
				ITile tile = ((TileView) arg.getSource()).getPieceModel();
					
				if (GameDataHandler.getInstance().getRules().isPossibleMove(troop.getPosition(), tile, troop)
						&& currentPlayer == troop.getPlayer()){
					troop.moveTo(tile);
					GameDataHandler.getInstance().getGui().updateMessage("Your army has moved.");
				}
			} 
			else if (lastClickedMouseButton == MouseEvent.BUTTON1 && o.getClass() == new TroopPiece().getClass() 
					&& mouseButtonClicked == MouseEvent.BUTTON3) {
				
			    ControlFactory.getControl(worldinruin.controllers.ControlFactory.Controllers.BATTLE_CONTROLLER);
				
				ITroop attackingTroop = (ITroop) ((TroopPiece) lastClickedComponent).getPieceModel();
				ITroop defendingTroop = (ITroop) ((ITilePiece) o).getPieceModel();
			
				attackingTroop.moveTo(defendingTroop.getPosition());
					
				Battle battle = new Battle(attackingTroop.getPlayer(), defendingTroop.getPlayer(), attackingTroop, defendingTroop);
				
				PanelController.getInstance().showPage(Panels.GAMEVIEWLEFTCARDSTACK, Panels.BATTLECOMMANDPANEL);
				PanelController.getInstance().showPage(Panels.GAMEVIEWRIGHTCARDSTACK, Panels.BATTLEMAINPANEL, battle);
			 }
				
		
		
		// Logic sequence to decide which panel to show on the left cardstack in the gameview
		
		if (mouseButtonClicked == MouseEvent.BUTTON1) {
			
			ITilePiece tilePiece =  ((ITilePiece) o);
			
			PanelController.getInstance().showPage(tilePiece);
			
			
		}
		
		currentTile = ((ITilePiece) o).getTileModel();
		lastClickedComponent = (Component) arg.getSource();
		lastClickedMouseButton = mouseButtonClicked;
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

}
